package com.echo.im.engin.netty.handler;


import com.echo.im.engin.event.entity.UserSessionRenewalEvent;
import com.echo.im.engin.netty.context.ChannelAttrKey;
import com.echo.im.engin.packet.HeartbeatPacket;
import com.echo.im.infrastructure.util.SpringContextHolder;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;

/**
 * userEventTriggered 方法可以处理多种不同的事件，这些事件不限于网络I/O相关的活动。
 * 所以要对到来的事件做一个判断，然后再决定是否处理
 * @author echo
 */
@Slf4j
public class HeartbeatHandler extends SimpleChannelInboundHandler<HeartbeatPacket> {

    public static final int HEARTBEAT_INTERVAL = 10;


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HeartbeatPacket packet) throws Exception {

        // 快速响应ws
        HeartbeatPacket heartbeatPacket= new HeartbeatPacket();
        ctx.channel().writeAndFlush(heartbeatPacket);

        // 更新心跳次数
        AttributeKey<Long> heartBeatAttr = AttributeKey.valueOf(ChannelAttrKey.HEARTBEAT_TIMES);
        Long heartbeatTimes = ctx.channel().attr(heartBeatAttr).get();
        ctx.channel().attr(heartBeatAttr).set(++heartbeatTimes);

        if (heartbeatTimes%HEARTBEAT_INTERVAL == 0){
            // 每心跳10次，用户在线状态续一次命
            //TODO 后期改为本地续命 因为现在更换以前的redis存储字符串的方案了，现在已经改为map
            AttributeKey<Long> userIdAttr = AttributeKey.valueOf(ChannelAttrKey.USER_ID);
            Long userId = ctx.channel().attr(userIdAttr).get();
            AttributeKey<Integer> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
            Integer terminal = ctx.channel().attr(terminalAttr).get();

            ApplicationEventPublisher applicationEventPublisher = SpringContextHolder.getApplicationEventPublisher();
            applicationEventPublisher.publishEvent(new UserSessionRenewalEvent(userId,terminal));
        }

        AttributeKey<Long> userIdAttr = AttributeKey.valueOf(ChannelAttrKey.USER_ID);
        Long userId = ctx.channel().attr(userIdAttr).get();
        log.info("心跳,userId:{},{}",userId,ctx.channel().id().asLongText());

    }

    /**
     * 对端心跳包超时了
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleState state = ((IdleStateEvent) evt).state();
            if (state == IdleState.READER_IDLE) {
                // 在规定时间内没有收到客户端的上行数据, 主动断开连接
                AttributeKey<Long> attr = AttributeKey.valueOf("USER_ID");
                Long userId = ctx.channel().attr(attr).get();
                AttributeKey<Integer> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
                Integer terminal = ctx.channel().attr(terminalAttr).get();
                log.info("心跳超时，即将断开连接,用户id:{},终端类型:{} ", userId, terminal);
                ctx.channel().close();
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }

    }


}
